/*
 * 
 * Copyright (c) 2006-2009 Sam Collett (http://www.texotela.co.uk) Dual licensed
 * under the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
 * (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * 
 * Version 2.2.4 Demo: http://www.texotela.co.uk/code/jquery/select/
 * 
 * $LastChangedDate$ $Rev$
 * 
 */

;
(function($) {

	/**
	 * Adds (single/multiple) options to a select box (or series of select
	 * boxes)
	 * 
	 * @name addOption
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type jQuery
	 *       @example
	 *       $("#myselect").addOption("Value", "Text"); // add single value (will be selected)
	 *       @example
	 *       $("#myselect").addOption("Value 2", "Text 2", false); // add single
	 *       value (won't be selected)
	 *       @example
	 *       $("#myselect").addOption({"foo":"bar","bar":"baz"}, false); // add multiple values, but don't select
	 * 
	 */
	$.fn.addOption = function() {
		var add = function(el, v, t, sO, p,f) {
			var option = document.createElement("option");
			option.value = v, option.text = t;
			$(option).attr("data-type", p);
			$(option).attr("data-phone",f);
			// get options
			var o = el.options;
			// get number of options
			var oL = o.length;
			if (!el.cache) {
				el.cache = {};
				// loop through existing options, adding to cache
				for (var i = 0; i < oL; i++) {
					el.cache[o[i].value] = i;
				}
			}
			// add to cache if it isn't already
			if (typeof el.cache[v] == "undefined")
				el.cache[v] = oL;
			el.options[el.cache[v]] = option;
			if (sO) {
				option.selected = true;
			}
		};

		var a = arguments;
		if (a.length == 0)
			return this;
		// select option when added? default is true
		var sO = true;
		// multiple items
		var m = false;
		// other variables
		var items, v, t;
		var p = "";
		var ph="";
		if (typeof(a[0]) == "object") {
			m = true;
			items = a[0];
		}
		if (a.length >= 2) {
			if (typeof(a[1]) == "boolean")
				sO = a[1];
			else if (typeof(a[2]) == "boolean")
				sO = a[2];
			if (!m) {
				v = a[0];
				t = a[1];

			}
			if (typeof a[3] != 'undefined') {
				p = a[3];
			}
			if(typeof a[4] != 'undefined'){
				ph=a[4];
			}
		}
		
		this.each(function() {
					if (this.nodeName.toLowerCase() != "select")
						return;
					if (m) {
						for (var item in items) {
							add(this, item, items[item], sO, p,ph);
						}
					} else {
						add(this, v, t, sO, p,ph);
					}
				});
		return this;
	};

	/**
	 * Add options via ajax
	 * 
	 * @name ajaxAddOption
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type jQuery
	 * @param String
	 *            url Page to get options from (must be valid JSON)
	 * @param Object
	 *            params (optional) Any parameters to send with the request
	 * @param Boolean
	 *            select (optional) Select the added options, default true
	 * @param Function
	 *            fn (optional) Call this function with the select object as
	 *            param after completion
	 * @param Array
	 *            args (optional) Array with params to pass to the function
	 *            afterwards
	 *            @example
	 *            $("#myselect").ajaxAddOption("myoptions.php");
	 *            @example
	 *            $("#myselect").ajaxAddOption("myoptions.php", {"code" :
	 *            "007"});
	 *            @example
	 *            $("#myselect").ajaxAddOption("myoptions.php", {"code" : "007"}, false, sortoptions, [{"dir": "desc"}]);
	 * 
	 */
	$.fn.ajaxAddOption = function(url, params, select, fn, args) {
		if (typeof(url) != "string")
			return this;
		if (typeof(params) != "object")
			params = {};
		if (typeof(select) != "boolean")
			select = true;
		this.each(function() {
					var el = this;
					$.getJSON(url, params, function(r) {
								$(el).addOption(r, select);
								if (typeof fn == "function") {
									if (typeof args == "object") {
										fn.apply(el, args);
									} else {
										fn.call(el);
									}
								}
							});
				});
		return this;
	};

	/**
	 * Removes an option (by value or index) from a select box (or series of
	 * select boxes)
	 * 
	 * @name removeOption
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type jQuery
	 * @param String|RegExp|Number
	 *            what Option to remove
	 * @param Boolean
	 *            selectedOnly (optional) Remove only if it has been selected
	 *            (default false)
	 *            @example
	 *            $("#myselect").removeOption("Value"); // remove by value
	 *            @example
	 *            $("#myselect").removeOption(/^val/i); // remove options with a
	 *            value starting with 'val'
	 *            @example
	 *            $("#myselect").removeOption(/./); // remove all options
	 *            @example
	 *            $("#myselect").removeOption(/./, true); // remove all options
	 *            that have been selected
	 *            @example
	 *            $("#myselect").removeOption(0); // remove by index
	 *            @example
	 *            $("#myselect").removeOption(["myselect_1","myselect_2"]); //
	 *            values contained in passed array
	 * 
	 */
	$.fn.removeOption = function() {
		var a = arguments;
		if (a.length == 0)
			return this;
		var ta = typeof(a[0]);
		var v, index;
		// has to be a string or regular expression (object in IE, function in
		// Firefox)
		if (ta == "string" || ta == "object" || ta == "function") {
			v = a[0];
			// if an array, remove items
			if (v.constructor == Array) {
				var l = v.length;
				for (var i = 0; i < l; i++) {
					this.removeOption(v[i], a[1]);
				}
				return this;
			}
		} else if (ta == "number")
			index = a[0];
		else
			return this;
		this.each(function() {
					if (this.nodeName.toLowerCase() != "select")
						return;
					// clear cache
					if (this.cache)
						this.cache = null;
					// does the option need to be removed?
					var remove = false;
					// get options
					var o = this.options;
					if (!!v) {
						// get number of options
						var oL = o.length;
						for (var i = oL - 1; i >= 0; i--) {
							if (v.constructor == RegExp) {
								if (o[i].value.match(v)) {
									remove = true;
								}
							} else if (o[i].value == v) {
								remove = true;
							}
							// if the option is only to be removed if selected
							if (remove && a[1] === true)
								remove = o[i].selected;
							if (remove) {
								o[i] = null;
							}
							remove = false;
						}
					} else {
						// only remove if selected?
						if (a[1] === true) {
							remove = o[index].selected;
						} else {
							remove = true;
						}
						if (remove) {
							this.remove(index);
						}
					}
				});
		return this;
	};

	/**
	 * Sort options (ascending or descending) in a select box (or series of
	 * select boxes)
	 * 
	 * @name sortOptions
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type jQuery
	 * @param Boolean
	 *            ascending (optional) Sort ascending (true/undefined), or
	 *            descending (false)
	 *            @example // ascending
	 * $("#myselect").sortOptions(); // or $("#myselect").sortOptions(true);
	 *            @example //
	 *            descending $("#myselect").sortOptions(false);
	 * 
	 */
	$.fn.sortOptions = function(ascending) {
		// get selected values first
		var sel = $(this).selectedValues();
		var a = typeof(ascending) == "undefined" ? true : !!ascending;
		this.each(function() {
					if (this.nodeName.toLowerCase() != "select")
						return;
					// get options
					var o = this.options;
					// get number of options
					var oL = o.length;
					// create an array for sorting
					var sA = [];
					// loop through options, adding to sort array
					for (var i = 0; i < oL; i++) {
						sA[i] = {
							v : o[i].value,
							t : o[i].text
						}
					}
					// sort items in array
					sA.sort(function(o1, o2) {
								// option text is made lowercase for case
								// insensitive sorting
								o1t = o1.t.toLowerCase(), o2t = o2.t
										.toLowerCase();
								// if options are the same, no sorting is needed
								if (o1t == o2t)
									return 0;
								if (a) {
									return o1t < o2t ? -1 : 1;
								} else {
									return o1t > o2t ? -1 : 1;
								}
							});
					// change the options to match the sort array
					for (var i = 0; i < oL; i++) {
						o[i].text = sA[i].t;
						o[i].value = sA[i].v;
					}
				}).selectOptions(sel, true); // select values, clearing
												// existing ones
		return this;
	};
	/**
	 * Selects an option by value
	 * 
	 * @name selectOptions
	 * @author Mathias Bank (http://www.mathias-bank.de), original function
	 * @author Sam Collett (http://www.texotela.co.uk), addition of regular
	 *         expression matching
	 * @type jQuery
	 * @param String|RegExp|Array
	 *            value Which options should be selected can be a string or
	 *            regular expression, or an array of strings / regular
	 *            expressions
	 * @param Boolean
	 *            clear Clear existing selected options, default false
	 *            @example
	 *            $("#myselect").selectOptions("val1"); // with the value 'val1'
	 *            @example
	 *            $("#myselect").selectOptions(["val1","val2","val3"]); // with
	 *            the values 'val1' 'val2' 'val3'
	 *            @example
	 *            $("#myselect").selectOptions(/^val/i); // with the value starting with 'val', case insensitive
	 * 
	 */
	$.fn.selectOptions = function(value, clear) {
		var v = value;
		var vT = typeof(value);
		// handle arrays
		if (vT == "object" && v.constructor == Array) {
			var $this = this;
			$.each(v, function() {
						$this.selectOptions(this, clear);
					});
		};
		var c = clear || false;
		// has to be a string or regular expression (object in IE, function in
		// Firefox)
		if (vT != "string" && vT != "function" && vT != "object")
			return this;
		this.each(function() {
					if (this.nodeName.toLowerCase() != "select")
						return this;
					// get options
					var o = this.options;
					// get number of options
					var oL = o.length;
					for (var i = 0; i < oL; i++) {
						if (v.constructor == RegExp) {
							if (o[i].value.match(v)) {
								o[i].selected = true;
							} else if (c) {
								o[i].selected = false;
							}
						} else {
							if (o[i].value == v) {
								o[i].selected = true;
							} else if (c) {
								o[i].selected = false;
							}
						}
					}
				});
		return this;
	};

	/**
	 * Copy options to another select
	 * 
	 * @name copyOptions
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type jQuery
	 * @param String
	 *            to Element to copy to
	 * @param String
	 *            which (optional) Specifies which options should be copied -
	 *            'all' or 'selected'. Default is 'selected'
	 *            @example
	 *            $("#myselect").copyOptions("#myselect2"); // copy selected options from 'myselect' to 'myselect2'
	 *            @example
	 *            $("#myselect").copyOptions("#myselect2","selected"); // same
	 *            as above
	 *            @example
	 *            $("#myselect").copyOptions("#myselect2","all"); // copy all options from 'myselect' to 'myselect2'
	 * 
	 */
	$.fn.copyOptions = function(to, which) {
		var w = which || "selected";
		if ($(to).size() == 0)
			return this;
		this.each(function() {
					if (this.nodeName.toLowerCase() != "select")
						return this;
					// get options
					var o = this.options;
					// get number of options
					var oL = o.length;
					for (var i = 0; i < oL; i++) {
						if (w == "all" || (w == "selected" && o[i].selected)) {
							$(to).addOption(o[i].value, o[i].text);
						}
					}
				});
		return this;
	};

	/**
	 * Checks if a select box has an option with the supplied value
	 * 
	 * @name containsOption
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type Boolean|jQuery
	 * @param String|RegExp
	 *            value Which value to check for. Can be a string or regular
	 *            expression
	 * @param Function
	 *            fn (optional) Function to apply if an option with the given
	 *            value is found. Use this if you don't want to break the
	 *            chaining
	 *            @example
	 *            if($("#myselect").containsOption("val1")) alert("Has an option with the value 'val1'");
	 *            @example
	 *            if($("#myselect").containsOption(/^val/i)) alert("Has an
	 *            option with the value starting with 'val'");
	 *            @example
	 *            $("#myselect").containsOption("val1", copyoption).doSomethingElseWithSelect(); // calls copyoption (user defined function) for any options found, chain is continued
	 * 
	 */
	$.fn.containsOption = function(value, fn) {
		var found = false;
		var v = value;
		var vT = typeof(v);
		var fT = typeof(fn);
		// has to be a string or regular expression (object in IE, function in
		// Firefox)
		if (vT != "string" && vT != "function" && vT != "object")
			return fT == "function" ? this : found;
		this.each(function() {
					if (this.nodeName.toLowerCase() != "select")
						return this;
					// option already found
					if (found && fT != "function")
						return false;
					// get options
					var o = this.options;
					// get number of options
					var oL = o.length;
					for (var i = 0; i < oL; i++) {
						if (v.constructor == RegExp) {
							if (o[i].value.match(v)) {
								found = true;
								if (fT == "function")
									fn.call(o[i], i);
							}
						} else {
							if (o[i].value == v) {
								found = true;
								if (fT == "function")
									fn.call(o[i], i);
							}
						}
					}
				});
		return fT == "function" ? this : found;
	};

	/**
	 * Returns values which have been selected
	 * 
	 * @name selectedValues
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type Array
	 *       @example
	 *       $("#myselect").selectedValues();
	 * 
	 */
	$.fn.selectedValues = function() {
		var v = [];
		this.selectedOptions().each(function() {
					v[v.length] = this.value;
				});
		return v;
	};

	/**
	 * Returns text which has been selected
	 * 
	 * @name selectedTexts
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type Array
	 *       @example
	 *       $("#myselect").selectedTexts();
	 * 
	 */
	$.fn.selectedTexts = function() {
		var t = [];
		this.selectedOptions().each(function() {
					t[t.length] = this.text;
				});
		return t;
	};

	/**
	 * Returns options which have been selected
	 * 
	 * @name selectedOptions
	 * @author Sam Collett (http://www.texotela.co.uk)
	 * @type jQuery
	 *       @example
	 *       $("#myselect").selectedOptions();
	 * 
	 */
	$.fn.selectedOptions = function() {
		return this.find("option:selected");
	};

})(jQuery);